Skip to content

[Taken]Fix issue #783 & #764#805

Merged
Nehon merged 2 commits into
jMonkeyEngine:masterfrom
jmecn:master
Jan 21, 2018
Merged

[Taken]Fix issue #783 & #764#805
Nehon merged 2 commits into
jMonkeyEngine:masterfrom
jmecn:master

Conversation

@jmecn
Copy link
Copy Markdown
Contributor

@jmecn jmecn commented Jan 14, 2018

Fix issue #783 & #764

store.x = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.y = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.z = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.addLocal(center);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it creates a random point within a box of the desired side length (=radius) around the center.
The Do-while loop is still necessary to skip the samples outside the sphere

Copy link
Copy Markdown
Contributor

@Nehon Nehon Jan 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhhh... wouldn't it be simpler to do;

     store.x = FastMath.nextRandomFloat();
     store.y = FastMath.nextRandomFloat();
     store.z = FastMath.nextRandomFloat();
     store.normalizeLocal(); // <- random direction
     store.multLocal(radius); // <- mult by the radius to be inside the sphere radius
     store.addLocal(center); // <- relocate around the center.

No loop needed and the normalize ensure that the point is inside the sphere.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be simpler, but would not be uniformly distributed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh yeah first the x2-1 should stay to allow negative values. But then just take à random length between 0 and radius, and you break the uniformity.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be wrong, but I think there is needs to more probability mass on the points with bigger radius than on the inner points. So if you normalize and then scale with a value between 0 and radius, that distribution must not be uniform, but something like u'=sqrt(1-u^2), as TehLeo suggested.

@TehLeo
Copy link
Copy Markdown
Contributor

TehLeo commented Jan 14, 2018

Yes, just as shaman pointed out, it got to pick an uniform point on a sphere. The current method, picks a random point in a box, repeatedly until the point is in the sphere. It forgets to add the center.

Here are two methods:

 do {
    store.x = (FastMath.nextRandomFloat() * 2f - 1f);
    store.y = (FastMath.nextRandomFloat() * 2f - 1f);
    store.z = (FastMath.nextRandomFloat() * 2f - 1f);
} while (store.dot(store) > 1);
store.multLocal(radius);
store.addLocal(center);

Or a method without a while loop. (ref: https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability )

float l = FastMath.pow(FastMath.nextRandomFloat(), 1f/3f);
float u = FastMath.nextRandomFloat()*2f-1f;
float o = FastMath.nextRandomFloat()*FastMath.TWO_PI;
		
store.z = radius*l*u;
u = FastMath.sqrt(1f-u*u);
		
store.x = radius*l*u*FastMath.cos(o);
store.y = radius*l*u*FastMath.sin(o);
store.addLocal(center);

Copy link
Copy Markdown
Contributor

@Nehon Nehon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getRandomrPoint must be changed.

As a rule of thumb, for the future, I prefer you do one separate PR for each issue. It's a lot simpler to review.

store.x = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.y = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.z = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
store.addLocal(center);
Copy link
Copy Markdown
Contributor

@Nehon Nehon Jan 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhhh... wouldn't it be simpler to do;

     store.x = FastMath.nextRandomFloat();
     store.y = FastMath.nextRandomFloat();
     store.z = FastMath.nextRandomFloat();
     store.normalizeLocal(); // <- random direction
     store.multLocal(radius); // <- mult by the radius to be inside the sphere radius
     store.addLocal(center); // <- relocate around the center.

No loop needed and the normalize ensure that the point is inside the sphere.

@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 14, 2018

I see. Next time I will make separated PR.
Just modified the code, please check it.

@TehLeo
Copy link
Copy Markdown
Contributor

TehLeo commented Jan 14, 2018

I've checked your changes. It creates random points within a sphere volume.
However, the points are not uniformly distributed.

See the two methods I posted in the comment above.

@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 14, 2018

ok, let me see..

@TehLeo
Copy link
Copy Markdown
Contributor

TehLeo commented Jan 14, 2018

Almost there. Except the line:

store.multLocal(FastMath.nextRandomFloat() * radius);

I have to say you are really trying to change the code I provided, which is fine, but try to understand the problem as well.

The random point was already selected uniformly within unit sphere volume within the while loop.
What is left to do is multiply it by radius and add the center.

Thus change that line back to.
store.multLocal(radius);

@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 15, 2018

Oh my.. I finally understand that line now.

@Nehon
Copy link
Copy Markdown
Contributor

Nehon commented Jan 15, 2018

@TehLeo good for you?

@TehLeo
Copy link
Copy Markdown
Contributor

TehLeo commented Jan 15, 2018

Yes, all is fine with #764 .

@Nehon
Copy link
Copy Markdown
Contributor

Nehon commented Jan 15, 2018

@jmecn Could you please rebase -i your PR so that it's only 2 commits one for each issue?
Do you know how to use rebase -i?

@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 16, 2018

@Nehon never use rebase before. But I think I can have a try.

@Nehon
Copy link
Copy Markdown
Contributor

Nehon commented Jan 16, 2018

Here is a nice an thorough explanation https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History

Edit: that's why I prefer 1 issue per PR because Github allows me to squash all your commits into one.
But for this PR I'd rather have 2 commits, one for each issue. So you have to clean up yourself ;)

jmecn added 2 commits January 17, 2018 08:52
Fix infinity loop in EmitterSphereShape. issue #764

I test on both method:

    public void getRandomPoint1(Vector3f store) {
        float l = FastMath.pow(FastMath.nextRandomFloat(), 1f / 3f);
        float u = FastMath.nextRandomFloat() * 2f - 1f;
        float o = FastMath.nextRandomFloat() * FastMath.TWO_PI;

        store.z = l * u;
        u = 1f / FastMath.fastInvSqrt(1f - u * u);
        store.x = l * u * FastMath.cos(o);
        store.y = l * u * FastMath.sin(o);
        store.multLocal(radius);
        store.addLocal(center);
    }

    public void getRandomPoint2(Vector3f store) {
        do {
            store.x = (FastMath.nextRandomFloat() * 2f - 1f);
            store.y = (FastMath.nextRandomFloat() * 2f - 1f);
            store.z = (FastMath.nextRandomFloat() * 2f - 1f);
        } while (store.lengthSquared() > 1);
        store.multLocal(radius);
        store.addLocal(center);
    }
    // Test
    public void testGetRandomPoint() {
        int n = 1000000;
        long start = System.nanoTime();
        for (int i = 0; i < n; i++) {
            getRandomPoint1(store);
        }
        long time1 = System.nanoTime() - start;

        start = System.nanoTime();
        for (int i = 0; i < n; i++) {
            getRandomPoint2(store);
        }
        long time2 = System.nanoTime() - start;

        System.out.println("t1:" + time1);
        System.out.println("t2:" + time2);
        System.out.println("t1/t2:" + (float) time1 / time2);
    }

Result:

    t1:352272158
    t2:94436324
    t1/t2:3.7302613

Method2 seems nearly 4 times faster than method1.
@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 17, 2018

@Nehon sir I have conbined the commits, please check my code.

@Nehon
Copy link
Copy Markdown
Contributor

Nehon commented Jan 17, 2018

Perfect, nice work.
I'll merge this PR once the build pass

@jmecn
Copy link
Copy Markdown
Contributor Author

jmecn commented Jan 20, 2018

Why this pr build failed? any reason?

@empirephoenix
Copy link
Copy Markdown
Contributor

Seems like an error with the buildserver (all buildlogs are null/empty) , I suggest to just commit a extra space or similar somewhere, so that it is triggered again.

@Nehon
Copy link
Copy Markdown
Contributor

Nehon commented Jan 21, 2018

No don't commit again I'll relaunch the build

@Nehon Nehon merged commit 1b2c84d into jMonkeyEngine:master Jan 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants